using System;
using System.Collections.Generic;
using System.Linq;

using EMan.Common.EnterpriseStructure;
using EMan.Interface.EnterpriseStructure.Departments;

using TrueCode.Core;
using TrueCode.Core.Aspects;
using TrueCode.Core.EditableEntity;

namespace EMan.BL.EnterpriseStructure
{
  public static class StructureChangesetManager
  {
   public static StructureChangeset MakeChangeset( IEnumerable< DepartmentEditable > treeRoots )
    {
      var builder = new ChangesetBuilder( treeRoots );
      return builder.Build();
    }

    public static void MergeChangeset( StructureChangeset changeset, ICollection< DepartmentEditable > treeRoots )
    {
//      var deletedNodes = new List< DepartmentEditable >();
//      foreach( var deletedDepartmentId in changeset.DeletedDepartments )
//        DFS( treeRoots, node => node.Entity.Id == deletedDepartmentId, deletedNodes.Add );
//
//      foreach( var deletedNode in deletedNodes )
//      {
//        if( deletedNode.ParentDepartment == null )
//          treeRoots.Remove( deletedNode );
//        else
//        {
//        }
//      }
//
//      foreach( var department in changeset.NewDepartments )
//        DFS( treeRoots,
//          node => node.Entity.Key == department.Key,
//          node => MergeTreeOfNewDepartments( node, department ) );
//
//      foreach( var department in changeset.ModifiedDepartments )
//        DFS( treeRoots, 
//          node => node.Entity.Id == department.Id, 
//          node => {
//                    node.UpdateAll( department );
//                    node.AcceptChanges(); 
//                  } );
    }

    private static void DFS( IEnumerable< DepartmentEditable > roots, Predicate< DepartmentEditable > predicate, Action< DepartmentEditable > action )
    {
      foreach( var node in roots )
        DFS( node, predicate, action );
    }

    private static void DFS( DepartmentEditable root, Predicate< DepartmentEditable > predicate, Action< DepartmentEditable > action )
    {
//      if( predicate( root ) )
//        action( root );
//      else
//        DFS( root.Departments, predicate, action );
    }

    private static void MergeTreeOfNewDepartments( DepartmentEditable node, Department department )
    {
//      foreach( var subDepartment in department.Departments )
//        MergeTreeOfNewDepartments( node.Departments.Single( n => n.Entity.Key == subDepartment.Key ), subDepartment );
//
//      node.UpdateAll( department );
//      node.AcceptChanges();
    }

    #region class ChangesetBuilder
    /// <summary>
    /// It is a helper class used in order to do not pass a lot of parameters in method of handling of tree
    /// </summary>
    private class ChangesetBuilder
    {
      #region Fields
      private readonly IEnumerable< DepartmentEditable > _treeRoots;

      private readonly List< Department > _newDepartments = new List< Department >();
      private readonly List< Department > _modifiedDepartments = new List< Department >();
      private readonly List< int > _deletedDepartments = new List< int >();

      private readonly List< Employee > _newEmployees = new List< Employee >();
      private readonly List< Employee > _modifiedEmployees = new List< Employee >();
      private readonly List< int > _deletedEmployees = new List< int >();
      #endregion

      #region .ctor
      public ChangesetBuilder( [NotEmpty] IEnumerable< DepartmentEditable > treeRoots )
      {
        if( treeRoots == null || !treeRoots.Any() ) throw new ArgumentNullException( "treeRoots" );
        _treeRoots = treeRoots;
      }
      #endregion

      #region Entry points
      public StructureChangeset Build()
      {
        foreach( var root in _treeRoots )
          ProcessTreeBranch( root );
        return new StructureChangeset( _newDepartments, _modifiedDepartments, _deletedDepartments, _newEmployees, _modifiedEmployees, _deletedEmployees );
      }
      #endregion

      #region Private
      private void ProcessTreeBranch( DepartmentEditable branchRoot )
      {
//        // If a department is deleted, add it and all sub departments and all employees into collections of ids of deleted entities
//        // and do not process any children
//        if( branchRoot.State == EditableObjectState.Deleted )
//          AddDepartmentTreeToDeleted( branchRoot );
//        else if( branchRoot.State == EditableObjectState.New )
//        {
//          // If root of the branch is new, all its children (if presented) are new, so add a root with all children, and do not process children
//          _newDepartments.Add( branchRoot.Entity.WholeTreeCopy() );
//        }
//        else
//        {
//          // If department is dirty add it to the list without children
//          if( branchRoot.State == EditableObjectState.Modified )
//            _modifiedDepartments.Add( branchRoot.Entity.Copy() );
//
//          // Process all employees
//          foreach( var employeeNode in branchRoot.Employees )
//          {
//            var employee = employeeNode.Entity;
//            switch( employeeNode.State )
//            {
//              case EditableObjectState.New:
//                _newEmployees.Add( employee );
//                break;
//              case EditableObjectState.Modified:
//                _modifiedEmployees.Add( employeeNode.Entity );
//                break;
//              case EditableObjectState.Deleted:
//                _deletedEmployees.Add( employee.Id );
//                break;
//              case EditableObjectState.Original:
//                break;
//              default:
//                throw new ArgumentOutOfRangeException();
//            }
//          }
//
//          // Recursively process all sub-departments
//          foreach( var subNode in branchRoot.Departments )
//            ProcessTreeBranch( subNode );
//        }
      }

      /// <summary>
      /// Adds <paramref name="departmentNode"/> and whole retained tree into list of deleted departments recursively.
      /// </summary>
      /// <param name="departmentNode">Node - the root of the tree.</param>
      private void AddDepartmentTreeToDeleted( DepartmentEditable departmentNode )
      {
        // add sub departments first - they will be deleted first and FK violation is avoided
//        foreach( var subNode in departmentNode.Departments )
//          AddDepartmentTreeToDeleted( subNode );
//
//        foreach( var employeeNode in departmentNode.Employees )
//          _deletedEmployees.Add( employeeNode.Entity.Id );
//
//        _deletedDepartments.Add( departmentNode.Entity.Id );
      }
      #endregion
    }
    #endregion
  }
}